package docker

import (
	"context"
	"fmt"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/swarm"
	"github.com/docker/docker/client"

	"xindl/deployer/pkg/errors"
	"xindl/deployer/pkg/logger"
)

// 服务管理器
type ServiceManager struct {
	client  *client.Client
	monitor *ServiceMonitor
	log     *logger.Logger
}

// 创建服务管理器
func NewServiceManager(debug bool) (*ServiceManager, error) {
	// 创建Docker客户端
	cli, err := client.NewClientWithOpts(client.FromEnv)
	if err != nil {
		return nil, errors.WrapDockerError(err, "创建Docker客户端失败")
	}

	// 创建日志记录器
	log := logger.New(debug).WithPrefix("Docker服务")

	// 创建服务监控器
	monitor := NewServiceMonitor(cli, debug)

	return &ServiceManager{
		client:  cli,
		monitor: monitor,
		log:     log,
	}, nil
}

// 部署服务
func (m *ServiceManager) Deploy(ctx context.Context, config *ServiceConfig) error {
	// 验证配置
	if err := m.validateConfig(config); err != nil {
		return err
	}

	// 检查服务是否存在
	service, _, err := m.client.ServiceInspectWithRaw(ctx, config.Name, types.ServiceInspectOptions{})
	if err == nil {
		// 服务已存在，执行更新
		return m.updateService(ctx, &service, config)
	}

	// 创建新服务
	return m.createService(ctx, config)
}

// 验证服务配置
func (m *ServiceManager) validateConfig(config *ServiceConfig) error {
	if config == nil {
		return errors.NewConfigError("服务配置不能为空")
	}

	if config.Name == "" {
		return errors.NewConfigError("服务名称不能为空")
	}

	if config.Image == "" {
		return errors.NewConfigError("镜像名称不能为空")
	}

	if config.Port < MinPort || config.Port > MaxPort {
		return errors.NewConfigError("无效的端口号").
			WithContext("port", fmt.Sprintf("%d", config.Port))
	}

	return nil
}

// 创建新服务
func (m *ServiceManager) createService(ctx context.Context, config *ServiceConfig) error {
	spec := m.createServiceSpec(config)

	// 创建服务
	m.log.Info("创建服务: %s", config.Name)
	resp, err := m.client.ServiceCreate(ctx, spec, types.ServiceCreateOptions{})
	if err != nil {
		return errors.WrapDockerError(err, "创建服务失败").
			WithContext("service", config.Name)
	}

	// 等待服务启动并健康
	return m.waitForServiceHealthy(ctx, config.Name)
}

// 更新服务
func (m *ServiceManager) updateService(ctx context.Context, service *swarm.Service, config *ServiceConfig) error {
	spec := m.createServiceSpec(config)

	// 设置版本信息
	version := service.Meta.Version

	// 更新服务
	m.log.Info("更新服务: %s", config.Name)
	err := m.client.ServiceUpdate(ctx, service.ID, version, spec, types.ServiceUpdateOptions{})
	if err != nil {
		return errors.WrapDockerError(err, "更新服务失败").
			WithContext("service", config.Name)
	}

	// 等待服务更新完成并健康
	return m.waitForServiceHealthy(ctx, config.Name)
}

// 创建服务规格
func (m *ServiceManager) createServiceSpec(config *ServiceConfig) swarm.ServiceSpec {
	// 合并环境变量
	env := MergeEnvVars(map[string]string{
		"ENV":  config.Env["ENV"],
		"PORT": fmt.Sprintf("%d", config.Port),
	}, config.Env)

	spec := swarm.ServiceSpec{
		Annotations: swarm.Annotations{
			Name: config.Name,
			Labels: map[string]string{
				LabelServiceName: config.Name,
				LabelEnvironment: env["ENV"],
				LabelVersion:     env["VERSION"],
			},
		},
		TaskTemplate: swarm.TaskSpec{
			ContainerSpec: &swarm.ContainerSpec{
				Image: config.Image,
				Env:   m.formatEnv(env),
				StopGracePeriod: &DefaultStopGracePeriod,
			},
			Resources: &swarm.ResourceRequirements{
				Limits: &swarm.Resources{
					NanoCPUs:    int64(config.CPULimit * 1e9),
					MemoryBytes: config.MemoryLimit,
				},
				Reservations: &swarm.Resources{
					NanoCPUs:    int64(config.CPURequest * 1e9),
					MemoryBytes: config.MemoryRequest,
				},
			},
			RestartPolicy: &swarm.RestartPolicy{
				Condition:   swarm.RestartPolicyConditionOnFailure,
				Delay:      &DefaultRestartDelay,
				MaxAttempts: &MaxRestartAttempts,
			},
			LogDriver: &swarm.Driver{
				Name:    DefaultLogDriver,
				Options: DefaultLogOpts,
			},
		},
		Mode: swarm.ServiceMode{
			Replicated: &swarm.ReplicatedService{
				Replicas: &config.Replicas,
			},
		},
		EndpointSpec: &swarm.EndpointSpec{
			Ports: []swarm.PortConfig{
				{
					Protocol:      swarm.PortConfigProtocolTCP,
					TargetPort:    config.Port,
					PublishedPort: config.Port,
				},
			},
		},
		UpdateConfig: &swarm.UpdateConfig{
			Parallelism:   DefaultUpdateParallelism,
			Delay:         DefaultUpdateDelay,
			FailureAction: DefaultUpdateFailureAction,
			Order:         DefaultUpdateOrder,
		},
		RollbackConfig: &swarm.UpdateConfig{
			Parallelism:   DefaultUpdateParallelism,
			Delay:         DefaultUpdateDelay,
			FailureAction: DefaultUpdateFailureAction,
			Order:         DefaultUpdateOrder,
		},
	}

	// 添加健康检查
	if config.HealthCheck {
		spec.TaskTemplate.ContainerSpec.Healthcheck = &swarm.HealthConfig{
			Test:     []string{"CMD-SHELL", fmt.Sprintf("curl -f http://localhost:%d%s || exit 1", config.Port, config.HealthPath)},
			Interval: HealthCheckInterval,
			Timeout:  HealthCheckTimeout,
			Retries:  HealthCheckRetries,
		}
	}

	return spec
}

// 等待服务健康
func (m *ServiceManager) waitForServiceHealthy(ctx context.Context, serviceName string) error {
	// 创建带超时的上下文
	timeoutCtx, cancel := context.WithTimeout(ctx, HealthCheckTotalTimeout)
	defer cancel()

	// 等待服务健康
	if err := m.monitor.WaitForHealthy(timeoutCtx, serviceName, HealthCheckTotalTimeout); err != nil {
		// 获取最后的服务状态
		status, statusErr := m.monitor.GetServiceStatus(ctx, serviceName)
		if statusErr == nil {
			m.log.Error("服务未能达到健康状态: 运行中=%d/%d, 失败=%d, 更新状态=%s, 错误=%s",
				status.RunningTasks, status.DesiredTasks,
				status.FailedTasks, status.UpdateStatus, status.LastError)
		}
		return errors.WrapHealthError(err, "等待服务健康超时").
			WithContext("service", serviceName).
			WithContext("timeout", HealthCheckTotalTimeout.String())
	}

	return nil
}

// 回滚服务
func (m *ServiceManager) Rollback(ctx context.Context, serviceName string) error {
	m.log.Info("回滚服务: %s", serviceName)

	// 执行回滚
	err := m.client.ServiceRollback(ctx, serviceName, types.ServiceRollbackOptions{})
	if err != nil {
		return errors.WrapDockerError(err, "回滚服务失败").
			WithContext("service", serviceName)
	}

	// 等待回滚完成并健康
	return m.waitForServiceHealthy(ctx, serviceName)
}

// 删除服务
func (m *ServiceManager) Remove(ctx context.Context, serviceName string) error {
	m.log.Info("删除服务: %s", serviceName)
	err := m.client.ServiceRemove(ctx, serviceName)
	if err != nil {
		return errors.WrapDockerError(err, "删除服务失败").
			WithContext("service", serviceName)
	}
	return nil
}

// 获取服务状态
func (m *ServiceManager) GetStatus(ctx context.Context, serviceName string) (*ServiceStatus, error) {
	return m.monitor.GetServiceStatus(ctx, serviceName)
}

// 监控服务
func (m *ServiceManager) Watch(ctx context.Context, serviceName string) (<-chan *ServiceStatus, error) {
	return m.monitor.WatchService(ctx, serviceName)
}

// 格式化环境变量
func (m *ServiceManager) formatEnv(env map[string]string) []string {
	if env == nil {
		return nil
	}

	result := make([]string, 0, len(env))
	for k, v := range env {
		result = append(result, fmt.Sprintf("%s=%s", k, v))
	}
	return result
}
